﻿using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using NUnit.Framework;
using SessionHunter.DataAccess;
using SessionHunter.Model;
using SessionHunter.UnitTests.DataAccess;

namespace SessionHunter.UnitTests.IntegrationTests.DataAccess
{
    /// <summary>
    /// Tests the session repository.  This will actually pull sessions out, so the connection string needs to point
    /// to a database with actual sessions.
    /// </summary>
    [TestFixture]
    public class TestSessionRepository : TestBase
    {
        /// <summary>
        /// Tests when the number of sessions is limited
        /// </summary>
        [Test]
        public void GetCurrentSessions_TestLimitedSessions_ComesBackWith5()
        {
            var options = new ApplicationOptions();
            options.NumberOfSessions = 5;
            var sessionList = TestGetCurrentSessions(options);
            Assert.LessOrEqual(sessionList.Count, 5, "More sessions were returned than expected");
        }

        /// <summary>
        /// Tests the behaviour of the show expires flag being true
        /// </summary>
        [Test]
        public void GetCurrentSessions_ShowCreateExpiresIsTrue_ReturnsCreatedAndExpires()
        {
            var options = new ApplicationOptions();
            options.ShowCreateExpires = true;
            var sessionList = TestGetCurrentSessions(options);
            Assert.IsNotNull(sessionList[0].Created);
            Assert.IsNotNull(sessionList[0].Expires);
        }

        /// <summary>
        /// Tests the behaviour of the show expires flag beinf false
        /// </summary>
        [Test]
        public void GetCurrentSessions_TestShowCreateExpiresIsFalse_ReturnsMinValue()
        {
            var options = new ApplicationOptions();
            options.ShowCreateExpires = false;
            var sessionList = TestGetCurrentSessions(options);
            Assert.AreEqual(sessionList[0].Created, DateTime.MinValue);
            Assert.AreEqual(sessionList[0].Expires, DateTime.MinValue);
        }

        /// <summary>
        /// Tests the behaviour of the show locking option being true
        /// </summary>
        [Test]
        public void GetCurrentSessions_TestShowLockingTrue()
        {
            var options = new ApplicationOptions();
            options.ShowLockInfo = true;
            var sessionList = TestGetCurrentSessions(options);
            Assert.Greater(sessionList[0].LockDate, DateTime.MinValue);
            Assert.Greater(sessionList[0].LockDateLocal, DateTime.MinValue);
            Assert.Greater(sessionList[0].Timeout, 0);
        }

        /// <summary>
        /// Tests the behaviour of the show locking option being false
        /// </summary>
        [Test]
        public void GetCurrentSessions_TestShowLockingFalse()
        {
            var options = new ApplicationOptions();
            options.ShowLockInfo = false;
            var sessionList = TestGetCurrentSessions(options);
            Assert.AreEqual(sessionList[0].LockDate, DateTime.MinValue);
            Assert.AreEqual(sessionList[0].LockDateLocal, DateTime.MinValue);
            Assert.AreEqual(sessionList[0].Timeout, 0);
        }

        [Test]
        public void GetCurrentSessions_TestOrderBySessionId_CorrectOrder()
        {
            var options = new ApplicationOptions();

            // Test order by session id
            options.OrderBy = ApplicationOptions.OrderByParameter.SessionId;
            var sessionList = TestGetCurrentSessions(options);
            Assert.Greater(sessionList.Count, 1, "Not enough sessions in the database to allow the unit test to pass");
            Assert.Greater(sessionList[1].SessionId, sessionList[0].SessionId, "Order is not by session, ascending");
        }

        [Test]
        public void GetCurrentSessions_TestOrderByLargestSession_CorrectOrder()
        {
            var options = new ApplicationOptions();

            // Test order by largest session
            options.OrderBy = ApplicationOptions.OrderByParameter.LargestSession;
            var sessionList = TestGetCurrentSessions(options);
            Assert.Greater(sessionList.Count, 1, "Not enough sessions in the database to allow the unit test to pass");
            Assert.LessOrEqual(sessionList[1].Size, sessionList[0].Size, "Order is not by size, descending");
        }

        [Test]
        public void GetCurrentSessions_TestOrderByCreateDateLeastRecent_CorrectOrder()
        {
            var options = new ApplicationOptions();

            // Test order by create date (least recent)
            options.OrderBy = ApplicationOptions.OrderByParameter.CreateDateLeastRecent;
            var sessionList = TestGetCurrentSessions(options);
            Assert.Greater(sessionList.Count, 1, "Not enough sessions in the database to allow the unit test to pass");
            Assert.GreaterOrEqual(sessionList[1].Created, sessionList[0].Created, "Order is not by Created, ascending");
        }

        [Test]
        public void GetCurrentSessions_TestOrderByCreateDateMostRecent_CorrectOrder()
        {
            var options = new ApplicationOptions();

            // Test order by create date (most recent)
            options.OrderBy = ApplicationOptions.OrderByParameter.CreateDateMostRecent;
            var sessionList = TestGetCurrentSessions(options);
            Assert.Greater(sessionList.Count, 1, "Not enough sessions in the database to allow the unit test to pass");
            Assert.LessOrEqual(sessionList[1].Created, sessionList[0].Created, "Order is not by Created, descending");
        }

        /// <summary>
        /// Helper method to get sessions out
        /// </summary>
        /// <param name="options">The options to validate</param>
        /// <returns>At least 1 session</returns>
        private static IList<Model.SessionData> TestGetCurrentSessions(ApplicationOptions options)
        {
            Contract.Ensures(Contract.Result<IList<SessionData>>() != null, "Null session list returned");
            Contract.Assert(options != null, "No application options passed in");

            var sessionRepository = new SessionRepository(TestConnectionStringRepository.GetConnectionString);
            var sessionList = sessionRepository.GetCurrentSessions(options);
            Assert.Greater(sessionList.Count, 0, "No sessions were returned.  Verify your test ASPState database currently has active sessions.");
            return sessionList;
        }

        [Test]
        public void TestConnection_TestValidConnection_ReturnsNull()
        {
            var sessionRepository = new SessionRepository(TestConnectionStringRepository.GetConnectionString);
            var message = SessionRepository.TestConnection(TestConnectionStringRepository.GetConnectionString);
            Assert.IsNull(message, "Test connection failed with message: " + message);
        }

        [Test]
        public void GetApplicationData_GetApplications_ReturnData()
        {
            // Assemble
            var options = new ApplicationOptions();

            // Test order by create date (most recent)
            options.Application = true;
            var sessionRepository = new SessionRepository(TestConnectionStringRepository.GetConnectionString);

            // Act
            var applicationList = sessionRepository.GetApplicationData();

            // Assert
            Assert.Greater(applicationList.Count, 0, "No applications returned");
            foreach (var application in applicationList)
                Assert.NotNull(application, "Application was not populated");
        }

        [Test]
        public void MapApplicationData_MapData_ItemFound()
        {
            // Assemble
            Dictionary<int, string> applicationList;
            List<SessionData> sessionList;
            PrepareMapApplicationData(out applicationList, out sessionList);

            // Act
            var sessionRepository = new SessionRepository(TestConnectionStringRepository.GetConnectionString);
            sessionRepository.MapApplicationData(applicationList, sessionList);

            // Assert
            Assert.AreEqual("/a/w3svc/b/root", sessionList[0].Application, "Item was not mapped");
        }

        [Test]
        public void MapApplicationData_MapData_ItemNotFound()
        {
            // Assemble
            Dictionary<int, string> applicationList;
            List<SessionData> sessionList;
            PrepareMapApplicationData(out applicationList, out sessionList);

            // Act
            var sessionRepository = new SessionRepository(TestConnectionStringRepository.GetConnectionString);
            sessionRepository.MapApplicationData(applicationList, sessionList);

            // Assert
            Assert.AreEqual("12121212", sessionList[1].Application, "Item was not the original hex value");
        }

        private static void PrepareMapApplicationData(out Dictionary<int, string> applicationList, out List<SessionData> sessionList)
        {
            applicationList = new Dictionary<int, string>();
            applicationList.Add(729231826, "/a/w3svc/b/root");
            applicationList.Add(23, "/c/w3svc/d/root");
            applicationList.Add(24, "02020202");

            sessionList = new List<SessionData>();
            sessionList.Add(new SessionData("000000", 0)
            {
                Application = "2b7731d2"
            }
            );
            sessionList.Add(new SessionData("000001", 0)
            {
                Application = "12121212"
            }
            );
        }
    }
}
